#!/usr/bin/env bash

#
# Copyright 2023 The GUAC Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -euf -o pipefail

SCRIPT_DIR=$(cd "$(dirname "$0")"; pwd)
GUAC_DIR=$(cd "${SCRIPT_DIR}"/../../..; pwd)

LOG_DIR="${GUAC_DIR}/internal/testing/e2e/logs"
LOG_FILE="${LOG_DIR}/logfile.log"
SCHEMA_BACKUP="${LOG_DIR}/schema_backup.sql"

export PGPASSWORD=$POSTGRES_PASSWORD

# Create the log directory if it doesn't exist
mkdir -p "$LOG_DIR"

# Create the blobstore directory if it doesn't exist
mkdir -p /tmp/blobstore

echo "Script started" | tee -a "$LOG_FILE"

guac_data_hash="254d737ebc42ca32f95033e93370f5427e3fce64"

echo @@@@ Installing gql Python package
pip install gql[all]

echo @@@@ Cloning guac-data
pushd "$GUAC_DIR"
git clone https://github.com/guacsec/guac-data.git
pushd guac-data
git checkout $guac_data_hash
popd
popd

echo @@@@ Starting up guac server in background
go run "${GUAC_DIR}/cmd/guacgql" --gql-backend=ent &

echo -n "Waiting for guac server to start"
set +e
for _ in {1..36} ; do
  sleep 5
  curl -s http://localhost:8080/query >/dev/null 2>&1
  if [ $? -ne 7 ] ; then
    break
  fi
  echo -n "."
done
set -e
echo

curl -s http://localhost:8080/query >/dev/null 2>&1 || (echo "Guac server not running in time" && exit 1)
echo "Guac server started"

duplicate_schema() {
  echo @@@@ Duplicating schema
  pg_dump -h localhost -p 5432 -U guac -d guac -s > "$SCHEMA_BACKUP"
}

restore_schema() {
  echo @@@@ Restoring schema
  psql -h localhost -p 5432 -U guac -d guac -f "$SCHEMA_BACKUP"
}

wipe_data() {
  echo @@@@ Wiping data
  local retries=5
  local count=0
  local success=false

  while [ $count -lt $retries ]; do
    psql -h localhost -p 5432 -U guac -d guac -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;" && success=true && break
    count=$((count + 1))
    echo "Retrying wipe_data ($count/$retries)..."
    sleep 5
  done

  if [ "$success" = true ]; then
    restore_schema
  else
    echo "Error: Failed to wipe data after $retries attempts" | tee -a "$LOG_FILE"
    exit 1
  fi
}

go run ${GUAC_DIR}"/cmd/guacingest" --add-vuln-on-ingest=true --add-license-on-ingest --add-eol-on-ingest &
go run ${GUAC_DIR}"/cmd/guacone" collect deps_dev -p &
go run ${GUAC_DIR}"/cmd/guaccsub" &

# Define ingestion commands
declare -a ingestion_commands=(
  "go run ${GUAC_DIR}/cmd/guacone collect files ${GUAC_DIR}/guac-data/docs/ --add-vuln-on-ingest=true --add-license-on-ingest --add-eol-on-ingest"
  "go run ${GUAC_DIR}/cmd/guaccollect files ${GUAC_DIR}/guac-data/docs/ --service-poll=false"
)

declare -A queryValues

queryValues["PkgQ1"]='.packages |= sort'
queryValues["PkgQ2"]='.packages[].namespaces |= sort'
queryValues["PkgQ3"]='.packages[].namespaces |= sort_by(.namespace) | .packages[].namespaces[].names[].versions |= sort_by(.purl) | .packages[].namespaces[].names[].versions[].qualifiers |= sort_by(.key) | del(.. | .id?)'
queryValues["PkgQ4"]='del(.. | .id?)'
queryValues["IsDependencyQ1"]='.IsDependency |= sort'
queryValues["IsDependencyQ2"]='del(.. | .id?) | del(.. | .origin?)'
queryValues["PathQ1"]='del(.. | .id?) | del(.. | .origin?)'
queryValues["OSVQ1"]='del(.. | .id?)'
queryValues["CertifyVulnQ1"]='del(.. | .id?) | del(.. | .timeScanned?)'
queryValues["ArtifactsQ1"]='.artifacts |= sort'
queryValues["PkgQ9"]='.packages[].namespaces |= sort_by(.namespace) | .packages[].namespaces[].names[].versions |= sort_by(.id) | .packages[].namespaces[].names[].versions[].qualifiers |= sort_by(.key) | del(.. | .id?)'
queryValues["CertifyLegalQ1"]='del(.. | .id?) | del(.. | .timeScanned?) | del(.. | .origin?)'
queryValues["EndOfLifeQ1"]='
  (.HasMetadata[] | select(.key == "endoflife") | .subject.namespaces[].names[].versions[].qualifiers) |= sort_by(.key)
  | del(.. | .id?)
  | del(.. | .timeScanned?)
  | del(.. | .origin?)
'

# Define an indexed array to maintain the order of the queries
queryOrder=(
  "PkgQ1"
  "PkgQ2"
  "PkgQ3"
  "PkgQ4"
  "IsDependencyQ1"
  "IsDependencyQ2"
  "PathQ1"
  "OSVQ1"
  "CertifyVulnQ1"
  "ArtifactsQ1"
  "PkgQ9"
  "CertifyLegalQ1"
  "EndOfLifeQ1"
)

queries="${GUAC_DIR}/demo/graphql/queries.gql"

duplicate_schema

# Loop through ingestion commands
for command in "${ingestion_commands[@]}"; do
  echo @@@@ Running ingestion command: "$command" | tee -a "$LOG_FILE"
  eval "$command"

  echo @@@@ Waiting for 5 minutes
  sleep 300

  if [ $? -ne 0 ]; then
    echo "Error: Command '$command' failed" | tee -a "$LOG_FILE"
    exit 1
  fi

  echo @@@@ Running queries and validating output

  id1=$(gql-cli http://localhost:8080/query -o PkgQ5 < "$queries" | jq -r ' .packages[0].namespaces[0].names[0].id ')
  id2=$(gql-cli http://localhost:8080/query -o PkgQ6 < "$queries" | jq -r ' .packages[0].namespaces[0].names[0].id ')
  id3=$(gql-cli http://localhost:8080/query -o PkgQ7 < "$queries" | jq -r ' .packages[0].namespaces[0].names[0].id ')
  id4=$(gql-cli http://localhost:8080/query -o PkgQ8 < "$queries" | jq -r ' .packages[0].namespaces[0].names[0].versions[0].id ')

  for query in "${queryOrder[@]}"; do
    echo @@@@ Running query "$query" | tee -a "$LOG_FILE"
    if [ "$query" == "PathQ1" ]; then
      gql-cli http://localhost:8080/query -o "$query" -V subject:"${id1}" target:"${id2}" < "$queries" | jq "${queryValues[$query]}" > "${GUAC_DIR}/got${query}.json"
    else
      gql-cli http://localhost:8080/query -o "$query" < "$queries" | jq "${queryValues[$query]}" > "${GUAC_DIR}/got${query}.json"
    fi
    diff -u "${SCRIPT_DIR}/expect${query}.json" "${GUAC_DIR}/got${query}.json" | tee -a "$LOG_FILE"
  done

  # Run the additional query that doesn't follow the same format
  echo @@@@ Running query path.py | tee -a "$LOG_FILE"
  "${GUAC_DIR}/demo/graphql/path.py" "$id3" "$id4" | tail -n +2 | jq 'del(.. | .id?) | del(.. | .origin?) | del(.. | .qualifiers?)' > "${GUAC_DIR}/gotPathPy.json"
  diff -u "${SCRIPT_DIR}/expectPathPy.json" "${GUAC_DIR}/gotPathPy.json" | tee -a "$LOG_FILE"

  echo @@@@ Fully ingested command: "$command" | tee -a "$LOG_FILE"

  # Wipe data after each ingestion command
  wipe_data
done

# Note: graphql_playground is left running, CI will clean it up